package com.datasecops.scannerservices.service;

import com.datasecops.scannerservices.config.ConfigFilePath;
import com.datasecops.scannerservices.entity.CommonParam;
import com.datasecops.scannerservices.enumerate.DBType;
import com.datasecops.scannerservices.enumerate.EngineStatus;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * 扫描数据处理类.
 * @author: SunLingzhi
 * @time: 2022/8/29 10:59
 */
@Service
@Slf4j
public class ScanDataProcessServer {

    @Resource
    private ScannerEngineServer engineServer;

    private final String symbol = "&";

    /**
     * 数据落盘
     *
     * @param
     * @param commonParam
     * @param typeEnum
     * @param jobId
     * @param jobName
     * @param scannerID
     * @param linage
     * @return
     * @Description
     * @author SunLingzhi
     * @time 2022/8/18 17:43
     */
    public boolean tradingData(CommonParam param, LinkedHashMap<String, LinkedList<String>> sourceData, int col,
                               CommonParam commonParam, DBType typeEnum, String jobId, String jobName, String scannerID, String linage) {
        log.info("start tradingData sourceData size: " + sourceData.size());

        //基础path
        StringBuilder basicPath = new StringBuilder(ConfigFilePath.getScannerDirPath() + File.separator + param.getJobId() + File.separator + param.getUUId());
        //暂存目录
        StringBuilder filePath = new StringBuilder(basicPath);
        if (DBType.POSTGRESQL.name().equalsIgnoreCase(typeEnum.name()) || DBType.SQLSERVER.name().equalsIgnoreCase(typeEnum.name())){
            filePath.append(File.separator).append("dbdata_").append(param.getDbName()).append(File.separator).append(param.getModel()+symbol+param.getTableName()).append("_").append(col);
        }else {
            filePath.append(File.separator).append("dbdata_").append(param.getDbName()).append(File.separator).append(param.getTableName()).append("_").append(col);
        }
        File file = new File(filePath.toString());
        if (!file.exists()){
            file.mkdirs();
        }

        LinkedList<String> list;
        ArrayList<String> lists = new ArrayList<>();
        OutputStreamWriter out = null;
        filePath.append(File.separator).append("col_");
        StringBuilder tempPath;
        int colIndex = 0;

        //>>>>>>>>>>>扫描数据落盘
        for (Map.Entry<String, LinkedList<String>> entry : sourceData.entrySet()) {
            colIndex++;
            list = entry.getValue();
            list.add(0,entry.getKey());//第一行，为title
            lists.add(entry.getKey());
            //落盘
            tempPath = new StringBuilder(filePath);
            tempPath.append(colIndex).append(".txt");

            try {
                out = new OutputStreamWriter(new FileOutputStream(tempPath.toString()), StandardCharsets.UTF_8);
                BufferedWriter bw = new BufferedWriter(out);
                for (String s : list) {
                    bw.write(s);
                    bw.newLine();
                }
                bw.flush();
                bw.close();
            } catch (Exception e) {
                log.error("Failed Method tradingData error: " + e.getMessage());
                return false;
            }finally{
                if(out != null){
                    try {
                        out.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        //>>>>>>>>>>>说明XML文件落盘
        Set<Map.Entry<String, LinkedList<String>>> entries = sourceData.entrySet();
        Document document = DocumentHelper.createDocument();
        document.addProcessingInstruction("xml","version=\"1.0\" encoding=\"UTF-16LE\"");
        Element rootElem = document.addElement("TaskInfo");
        rootElem.addAttribute("Type","DB");

        //DB_INFO
        Element dbInfoElem = DocumentHelper.createElement("DBInfo");
        dbInfoElem.addAttribute("dbtype",param.getTypeEnum().name().replace("_","-"));//数据类型
        dbInfoElem.addAttribute("serverip",param.getIp());//数据库地址
        dbInfoElem.addAttribute("serverPort",param.getPort()+"");//端口
        dbInfoElem.addAttribute("dbname",param.getDbName());//数据库名称
        dbInfoElem.addAttribute("tablename",param.getTableName());//表名称
        dbInfoElem.addAttribute("tablerowcount",linage);//表总行数
        dbInfoElem.addAttribute("tableindex",col+"");//第几批次
        dbInfoElem.addAttribute("resguid",param.getUUId());//uuId
        if ("mongodb".equalsIgnoreCase(param.getTypeEnum().name())){
            dbInfoElem.addAttribute("db_relation_type","keyvalue");//关系型数据库
        }else{
            dbInfoElem.addAttribute("db_relation_type","RDBMS");//关系型数据库
        }
        rootElem.add(dbInfoElem);

        //TaskkBaseInfo
        Element taskkBaseInfo = DocumentHelper.createElement("TaskBaseInfo");
        taskkBaseInfo.addAttribute("TaskID",jobId);
        taskkBaseInfo.addAttribute("TaskName",jobName);
        taskkBaseInfo.addAttribute("ScannerID",scannerID);
        rootElem.add(taskkBaseInfo);

        //TaskkProgressInfo
        Element taskkProgressInfo = DocumentHelper.createElement("TaskProgressInfo");
        taskkProgressInfo.addAttribute("sum_row_count","0");//扫描任务总行数
        taskkProgressInfo.addAttribute("cur_row_pos","0");//当前任务节点所在的总行数
        taskkProgressInfo.addAttribute("cur_row_count","0");//
        rootElem.add(taskkProgressInfo);

        //DB_Policy
        Element dbPolicy = DocumentHelper.createElement("DBPolicy");
        dbPolicy.addAttribute("PolicyPath",commonParam.getPolicyPath());
        dbPolicy.addAttribute("FingerPath",commonParam.getFingerPath());
        dbPolicy.addAttribute("DictPath",commonParam.getDictPath());
        dbPolicy.addAttribute("MchClusterPath",commonParam.getMchClusterPath());
        rootElem.add(dbPolicy);

        //DB_DATA
        Element dbDataElem = DocumentHelper.createElement("DBData");
        Element colData;
        String path;
        for (int i = 1; i <= colIndex; i++) {
            colData = DocumentHelper.createElement("ColData");
            colData.addAttribute("name", lists.get(i - 1));
            path = filePath.toString() + i + ".txt";
            colData.addAttribute("Path",path);
            dbDataElem.add(colData);
        }
        rootElem.add(dbDataElem);

        OutputStreamWriter xmlWriter = null;
        StringBuilder xmlPath = new StringBuilder(basicPath);
        if (DBType.POSTGRESQL.name().equalsIgnoreCase(typeEnum.name()) || DBType.SQLSERVER.name().equalsIgnoreCase(typeEnum.name()) || DBType.DB2.name().equalsIgnoreCase(typeEnum.name())){
            xmlPath.append(File.separator).append("config_").append(param.getDbName()).append("_").append(param.getModel()).append(symbol).append(param.getTableName()).append("_").append(col).append(".xml");
        }else {
            xmlPath.append(File.separator).append("config_").append(param.getDbName()).append("_").append(param.getTableName()).append("_").append(col).append(".xml");
        }
       // XMLWriter writer = null;
        try {
            //String xmlStr = ByteStreamHelper.toXmlString(rootElem,StandardCharsets.UTF_8);
            //UTF-16LE，需要手动加入BOM头
            String xmlStr = rootElem.asXML();
            byte[] bom = {-1, -2};
            FileOutputStream outputStream = new FileOutputStream(xmlPath.toString());
            outputStream.write(bom);

            xmlWriter = new OutputStreamWriter(outputStream, StandardCharsets.UTF_16LE);
            BufferedWriter bw = new BufferedWriter(xmlWriter);

            //writer =  new XMLWriter(new FileOutputStream(xmlPath.toString()), (OutputFormat) document);
            bw.write(xmlStr);
            bw.flush();
            bw.close();
        } catch (IOException e) {
            log.error("Failed Method Write xml error: " + e.getMessage());
        }finally {
            if (null != xmlWriter){
                try {
                    xmlWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        //调用引擎传输数据
        engineServer.callScannerEngine(param.getJobId(), "scanproxy", EngineStatus.DATA.getValue(),xmlPath.toString());
        return true;
    }
}
